<template>
  <div class="page">
    <div class="l split-band"></div>
    <div class="main-wrap">
      <div class="top-ctrl-band">
        <div class="app-title">
          <div class="ctrl-item-name">捷登PC使用信息采集V1.0.2</div>
        </div>
        <div style="flex: 1"></div>
        <div style="font-size: 12px; color: #666; padding-right: 12px">服务器时间: {{ serverUptime | shortDatetime }}</div>
      </div>
      <div class="main-band">
        <div class="online-list">
          <div
            @click="handleClickClient(c)"
            :style="{ background: c.clientid == selectedClient.clientid ? '#EFEFEF' : '' }"
            class="client-info"
            v-for="c in clientList"
            :key="c.clientid"
          >
            <div class="client-id">员工:{{ c.workerName || '未提交' }}</div>
            <div class="connect-time">连接:{{ c.connected_at | shortDatetime }}</div>
            <div class="online-status" :style="{ color: c.online ? 'green' : '#BBB' }">{{ c.online ? '在线' : '离线' }}</div>
          </div>
        </div>
        <div class="detail-wrap">
          <template v-if="selectedClient.clientid">
            <div class="cur-client-info">
              <span style="margin-right: 6px; color: #555">ID:</span>
              <span style="margin-right: 6px; color: #888">{{ selectedClient.clientid }}</span>
              <span style="margin-right: 6px; color: #555"> 员工:</span>
              <span style="margin-right: 6px; color: #888">{{ selectedClient.workerName }}</span>
              <div style="flex: 1"></div>
              <span style="margin-right: 6px; color: #555"> 采样周期</span>
              <el-select :disabled="loadingCronConfig" v-model="curCron" placeholder="请选择">
                <el-option v-for="item in cronList" :key="item.label" :label="item.label" :value="item.value"> </el-option>
              </el-select>
              <div style="width: 6px"></div>
              <el-button :loading="loadingCronConfig" @click="modifyClientSettingsItem(0)" type="primary" size="mini">提交变更</el-button>
              <el-button @click="handleClickClient(selectedClient, true)" type="success" size="mini">全部刷新</el-button>
            </div>

            <div class="detail-band">
              <div class="detail-band-top">
                <span style="color: #666">当前活动应用</span>
                <span></span>
                <div style="flex: 1"></div>
                <el-button @click="handlePubToSelectedClient('getActiveWindow|getAppMemStatus')" type="primary" size="mini">刷新</el-button>
              </div>

              <div id="echarts-active-app-total" style="height: 500px"></div>
              <div id="echarts-active-app" style="height: 200px"></div>
            </div>
            <div class="detail-band">
              <div class="detail-band-top">
                <span style="color: #666">PC桌面截图</span>
                <span></span>
                <div style="flex: 1"></div>
                <el-button @click="handlePubToSelectedClient('captureScreen')" type="primary" size="mini">再截一张</el-button>
              </div>
              <div class="img-wrap" v-if="captureUrl">
                <el-image style="width: 100%" :src="captureUrl" :preview-src-list="[captureUrl]"> </el-image>
              </div>
              <div class="img-wrap" style="display: flex; align-items: center; justify-content: center" v-else>
                <span style="font-size: 12px; color: #409eff">正在获取截图...</span>
              </div>
            </div>
            <div class="detail-band"></div>
          </template>
        </div>
      </div>
      <div class="msg-list-wrap">
        <div class="msg-item" v-for="(item, index) in msgList" :key="index">
          <div class="msg-topic">
            {{ item.topic }} {{ item.message.action || '' }}
            <div v-if="!item.showDetail" @click="item.showDetail = true" style="margin-left: 10px; color: #409eff">查看详情</div>
          </div>
          <div v-if="item.showDetail" class="msg-content">{{ item.message }}</div>
        </div>
      </div>
      <div class="bottom-info-wrap">xiaox@2025 V1.0</div>
    </div>
    <div class="r split-band"></div>
  </div>
</template>

<script>
import request from '@/utils/request'
import { MqttHelper } from '@/utils/mqttHelper'
import * as echarts from 'echarts'
import { shortDatetime } from '@/filters'
var MM = null
function transformAndSort(data) {
  const transformed = Object.entries(data).map(([key, value]) => ({
    name: key,
    times: value,
  }))

  return transformed.sort((a, b) => a.times - b.times)
}
function convertSecondsToHoursMinutes(seconds) {
  if (seconds < 60) {
    return '1分钟'
  }

  const hours = Math.floor(seconds / 3600)
  const minutes = Math.floor((seconds % 3600) / 60)

  let formatted = ''
  if (hours > 0) {
    formatted += `${hours}小时`
  }
  if (minutes > 0) {
    formatted += `${minutes}分钟`
  }

  return formatted
}
export default {
  name: 'Jdzb',
  data() {
    return {
      msgList: [],
      clientList: [],
      selectedClient: {},
      activeAppChart: null,
      activeAppTotalChart: null,
      captureUrl: '',
      cronList: [
        { label: '每4秒', value: '*/4 * * * * *' },
        { label: '每30秒', value: '*/30 * * * * *' },
        { label: '每1分钟', value: '0 */1 * * * *' },
        { label: '每2分钟', value: '0 */2 * * * *' },
        { label: '每10分钟', value: '0 */10 * * * *' },
      ],
      loadingCronConfig: true,
      curCron: '',
      serverUptime: '',
    }
  },
  watch: {},
  created() {},
  mounted() {
    this.init()
  },
  destroyed() {
    // window.removeEventListener('storage', this.afterQRScan)
  },
  methods: {
    modifyClientSettingsItem() {
      // console.log(this.curCron)
      const topic = 'jdzb/pcmonitor/client/All'
      try {
        this.loadingCronConfig = true
        MM.doPublish({
          topic,
          qos: 0,
          payload: { action: 'modifyClientSettingsItem', key: 'cronExpression', value: this.curCron },
        })
      } catch (e) {
        console.log('Error:', e)
      }
    },

    loadActiveAppChart(opt, opt2) {
      if (!this.activeAppChart) {
        const chartDom = document.getElementById('echarts-active-app')
        if (!chartDom) return
        this.activeAppChart = echarts.init(chartDom)

        window.addEventListener('resize', () => {
          this.activeAppChart.resize()
        })
      }
      if (!this.activeAppTotalChart) {
        const chartDom = document.getElementById('echarts-active-app-total')
        if (!chartDom) return
        this.activeAppTotalChart = echarts.init(chartDom)

        window.addEventListener('resize', () => {
          this.activeAppTotalChart.resize()
        })
      }
      this.activeAppChart.setOption(opt)
      this.activeAppTotalChart.setOption(opt2)
    },

    handleClickClient(c, reset = false) {
      if (this.selectedClient.clientid == c.clientid && !reset) return
      if (this.activeAppChart) this.activeAppChart.clear()
      if (this.activeAppTotalChart) this.activeAppTotalChart.clear()

      this.captureUrl = ''
      this.selectedClient = c
      this.curCron = ''
      this.loadingCronConfig = true
      this.handlePubToSelectedClient('all')
    },

    handlePubToSelectedClient(action = 'all') {
      /*
        action = 'all' 发送所有指令      
      */
      const topic = 'jdzb/pcmonitor/client/' + this.selectedClient.clientid
      try {
        if (action == 'all' || action.indexOf('getActiveWindow') != -1) {
          MM.doPublish({
            topic,
            qos: 0,
            payload: { action: 'getActiveWindow' },
          })
        }

        if (action == 'all' || action.indexOf('getAppMemStatus') != -1) {
          MM.doPublish({
            topic,
            qos: 0,
            payload: { action: 'getAppMemStatus' },
          })
        }
        if (action == 'all' || action.indexOf('captureScreen') != -1) {
          MM.doPublish({
            topic,
            qos: 0,
            payload: { action: 'captureScreen' },
          })
        }
        if (action == 'all' || action.indexOf('getCronExpression') != -1) {
          MM.doPublish({
            topic,
            qos: 0,
            payload: { action: 'getCronExpression' },
          })
        }

        if (action == 'all' || action.indexOf('startCronTask') != -1) {
          MM.doPublish({
            topic,
            qos: 0,
            payload: { action: 'startCronTask' },
          })
        }
      } catch (e) {
        console.log('Error:', e)
      }
    },

    async init() {
      MM = new MqttHelper()
      MM.addFeedBackHandle(this.msgRouter)
      const lis = await this.handleGetClients()
      this.clientList = lis.map((e) => {
        return { ...e, online: true, workerName: null }
      })

      // 加载所有客户端的信息时，更新一次workerName
      MM.doPublish({
        topic: 'jdzb/pcmonitor/client/All',
        qos: 0,
        payload: { action: 'getMqttClientInfo' },
      })

      // this.loadActiveAppChart()
    },

    msgRouter({ topic, message }) {
      const actions = {
        handleConnected: () => {
          const client = this.clientList.find((e) => e.clientid == message.clientid)
          if (client) {
            client.online = true
          } else if (message.username == 'jdzb_common') {
            this.clientList.push({
              ...message,
              online: true,
            })
          }
        },

        handleDisconnected: () => {
          const client = this.clientList.find((e) => e.clientid == message.clientid)
          if (client) {
            client.online = false
          }
        },

        getMqttClientInfo: () => {
          const client = this.clientList.find((e) => e.clientid == message.cid)
          if (client) {
            client.workerName = message.data.workerName
          } else {
            this.updateClientList({ cid: message.cid, workerName: message.data.workerName })
          }
        },
        getActiveWindow: () => {
          console.log('getActiveWindow', message)
          /*
          const t = [
            { name: 'ConEmu64.exe', startTime: '2025-08-28 15:44:04.749', duration: 67 },
            { name: 'Code.exe', startTime: '2025-08-28 15:45:12.582', duration: 92 },
            { name: 'explorer.exe', startTime: '2025-08-28 15:46:44.924', duration: 4 },
           ..
          ]
          */
          const series = message.data.map((e) => {
            return {
              name: e.name,
              type: 'bar',
              stack: 'total',
              label: { show: true },
              emphasis: { focus: 'series' },
              data: [e.duration],
            }
          })

          const startdt = message.data[0].startTime
          const option = {
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'shadow',
              },
            },
            legend: {},
            xAxis: {
              type: 'value',
              axisLabel: {
                formatter: function (v) {
                  const c = (t) => {
                    return ('0' + t).substring(('0' + t).length - 2)
                  }
                  let dd = new Date(new Date(startdt).getTime() + v * 1000)
                  return `${c(dd.getHours())}:${c(dd.getMinutes())}:${c(dd.getSeconds())}`
                },
              },
            },
            yAxis: {
              type: 'category',
              data: ['活动应用'],
            },
            dataZoom: [
              {
                type: 'slider',
                show: true,
                xAxisIndex: [0],
                start: 0,
                end: 100,
                filterMode: 'none',
              },
              // {
              //   type: 'inside',
              //   xAxisIndex: [0],
              //   start: 0,
              //   end: 100,
              //   filterMode: 'none',
              // },
            ],
            itemStyle: {
              color: function (params) {
                var colorList = ['#5470c6', '#91cc75', '#5470c6', '#fa8c16', '#1890ff']
                return '#1890ff'
              },
            },
            series,
          }

          let res = message.data.reduce((pre, cur) => {
            if (!pre[cur.name]) pre[cur.name] = 0
            if (cur.duration) pre[cur.name] += cur.duration
            return pre
          }, {})
          console.log('#######res', res)

          res = transformAndSort(res)
          console.log('#######res', res)

          res = res.map((item) => {
            return {
              ...item,
              // zhtimes: convertSecondsToHoursMinutes(item.times),
            }
          })
          console.log('#######res', res)
          const option2 = {
            title: {
              text: '应用活动时间汇总',
            },
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'shadow',
              },
              formatter: function (t) {
                // console.log('t', t)
                return t[0].data + '[' + convertSecondsToHoursMinutes(t[0].data) + ']'
              },
            },
            legend: {},
            xAxis: {
              type: 'value',
              axisLabel: {
                formatter: convertSecondsToHoursMinutes,
              },
            },
            yAxis: {
              type: 'category',
              data: res.map((e) => e.name),
            },
            itemStyle: {
              color: function (params) {
                var colorList = ['#5470c6', '#91cc75', '#5470c6', '#fa8c16', '#1890ff']
                return colorList[params.dataIndex]
              },
            },
            series: [
              {
                type: 'bar',
                data: res.map((e) => e.times),
              },
            ],
          }

          this.loadActiveAppChart(option, option2)
        },

        getAppMemStatus: () => {
          console.log('getAppMemStatus', message)
        },

        captureScreen: () => {
          console.log('captureScreen', message)
          this.captureUrl = message.data.fname
        },

        getCronExpression: () => {
          console.log('getCronExpression', message)
          this.curCron = message.data
          this.loadingCronConfig = false
        },

        modifyClientSettingsItem: () => {
          console.log('modifyClientSettingsItem', message)
          this.loadingCronConfig = false
          this.handlePubToSelectedClient('startCronTask')
        },
      }

      if (!topic.endsWith('/datetime')) {
        this.msgList.push({ topic, message, showDetail: false })
      }

      let action = null

      if (topic.endsWith('/connected')) action = actions.handleConnected
      if (topic.endsWith('/disconnected')) action = actions.handleDisconnected
      if (topic.endsWith('/datetime')) {
        this.serverUptime = message
      }
      if (topic.startsWith('jdzb/pcmonitor/midService/feedBack')) {
        console.log('topic', topic, message)
        action = actions[message.action]
      }

      if (action) action()
    },

    async handleGetClients() {
      this.$ShowLoading()
      try {
        const res = await request({
          url: 'clients',
          method: 'get',
        })
        return res.data.data.filter((e) => e.username == 'jdzb_common')
      } catch (e) {
        console.log('Error:', e)
      } finally {
        this.$CloseLoading()
      }
    },

    updateClientList({ cid, workerName }) {
      console.log('updateClientList', cid, workerName)
      // todo:更新在线客户端列表
    },
  },
}
</script>

<style lang="scss" scoped>
.page {
  height: 100%;
  display: flex;
  padding: 10px 0;
  box-sizing: border-box;
}

.split-band {
  height: calc(100vh - 20px);
  flex: 1;
  // background-color: #f5f5f5;
}
.l {
  border-right: 1px solid #ddd;
}

.r {
  border-left: 1px solid #ddd;
}

.main-wrap {
  width: 1280px;
  min-width: 1280px;
  display: flex;
  flex-direction: column;
}

.top-ctrl-band {
  height: 60px;
  background-color: #faecd8;
  width: 100%;
  border-bottom: 1px solid #ddd;
  display: flex;
  align-items: center;
}

.app-title {
  font-size: 18px;
  font-weight: 500;
  color: #333;
  padding-left: 16px;
}

.main-band {
  height: calc(100vh - 300px);
  display: flex;
}

.online-list {
  width: 180px;
  background: #fff;
  border-right: 1px solid #ddd;
}

.detail-wrap {
  flex: 1;
  overflow: auto;
  position: relative;
}

.msg-list-wrap {
  height: 240px;
  font-size: 12px;
  color: #777;
  overflow: auto;
  padding: 8px;
  border-top: 1px solid #ddd;
  display: flex;
  align-items: baseline;
  flex-direction: column;
}

.bottom-info-wrap {
  height: 28px;
  background-color: #faecd8;
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-size: 10px;
}

.msg-item {
  // background: #bbb;
  border-radius: 4px;
  padding: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

.msg-item + .msg-item {
  margin-top: 6px;
}
.msg-topic {
  color: #555;
  margin-bottom: 2px;
  display: flex;
  align-items: center;
}

.msg-content {
  white-space: normal;
  word-wrap: break-word;
  word-break: break-word;
  overflow-wrap: break-word;
  hyphens: auto;
  line-height: 1.6;
  color: #888;
}

.client-info {
  display: flex;
  flex-direction: column;
  padding: 8px;
  border-bottom: 1px solid #ddd;
  position: relative;
  cursor: pointer;
}

.client-id {
  font-size: 14px;
  font-weight: 500;
  color: #555;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 8px;
}

.connect-time {
  font-size: 12px;
  color: #888;
}
.online-status {
  font-size: 10px;
  position: absolute;
  right: 8px;
  bottom: 6px;
}

.detail-band {
  display: flex;
  flex-direction: column;
}

.cur-client-info {
  position: sticky;
  top: 0;
  left: 0;
  height: 40px;
  background-color: #b3d8ff;
  width: 100%;
  border-bottom: 1px solid #eee;
  display: flex;
  align-items: center;
  padding: 0 12px;
  z-index: 999;
}

.img-wrap {
  background: #ddd;
  padding: 12px;
  box-sizing: border-box;
}

.detail-band-top {
  height: 40px;
  background-color: #fff;
  width: 100%;
  border-bottom: 1px solid #ddd;
  border-top: 1px solid #ddd;
  display: flex;
  align-items: center;
  padding: 0 12px;
}

#echarts-active-app {
  width: 96%;
  margin-left: 2%;
  height: 200px;
}

#echarts-active-app-total {
  width: 96%;
  margin-left: 2%;
  height: 400px;
}
</style>
